#####################################################################################
#
#  Copyright (c) Harry Pierson. All rights reserved.
#
# This source code is subject to terms and conditions of the Microsoft Public License. 
# A  copy of the license can be found at http://opensource.org/licenses/ms-pl.html
# By using this source code in any fashion, you are agreeing to be bound 
# by the terms of the Microsoft Public License.
#
# You must not remove this notice, or any other, from this software.
#
#####################################################################################

import ipypulldom
from System.Xml import XmlNodeType
  
class _type_factory(object):
  class _type_node(object):
    def __init__(self, node):
      ty = type(node)
      self.name = ty.__name__
      self.namespace = ty.xmlns
    
  def __init__(self):
    self.types = {}

  def find_type(self, node, parent):
    def create_type(node, parent): 
      return type(node.name, (parent,), {'xmlns':node.namespace})

    if parent not in self.types:
      self.types[parent] = {}
    
    tp = self.types[parent]
    if node.name not in tp:
      tp[node.name] = [create_type(node, parent)]
    
    tpn = tp[node.name]
    
    for t in tpn:
      if t.xmlns == node.namespace: 
        return t
    
    #if there's no matching namespace type, create one and add it to the list
    new_type = create_type(node, parent)
    tpn.append(new_type)
    return new_type
  
  def __call__(self, node, parent=object):
    if isinstance(node, ipypulldom.XmlNode):
      return self.find_type(node, parent) 
    
    return self.find_type(self._type_node(node), parent)


xtype = _type_factory()

  
def xml2py(nodelist):
  
  def children(nodelist):
    while True:
      child = xml2py(nodelist)
      if child is None:
        break
      yield child
  
  def set_attribute(parent, child):
    name = type(child).__name__
    if not hasattr(parent, name):
      setattr(parent, name, child)
    else:
      val = getattr(parent, name)
      if isinstance(val, list):
        val.append(child)
      else:
        setattr(parent, name, [val, child])
      
  node = nodelist.next()
  if node.nodeType == XmlNodeType.EndElement:
    return None
    
  elif node.nodeType == XmlNodeType.Text or node.nodeType == XmlNodeType.CDATA:
    return node.value
    
  elif node.nodeType == XmlNodeType.Element:
    #create a new object type named for the element name
    cur = xtype(node)()
    cur._nodetype = XmlNodeType.Element
   
    #collect all the attributes and children in lists
    attributes = [xtype(attr, str)(attr.value) for attr in node.attributes]
    children = [child for child in children(nodelist)]
    
    if len(children) == 1 and isinstance(children[0], str):
      #fold up elements with a single text node
      cur = xtype(cur, str)(children[0])
      cur._nodetype = XmlNodeType.Element
    else:
      #otherwise, add child elements as properties on the current node
      for child in children:
        set_attribute(cur, child)

    for attr in attributes:
      attr._nodetype = XmlNodeType.Attribute
      set_attribute(cur, attr)
             
    return cur


def parse(xml):
  return xml2py(ipypulldom.parse(xml))
  
def parseString(xml):
  return xml2py(ipypulldom.parseString(xml))
  

if __name__ == '__main__':  
  rss = parse('http://feeds.feedburner.com/Devhawk')
  for item in rss.channel.item:
    print item.title
  